//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2007
//
// Keyboard stuff.
//

# include "StdAfx.h"
# include "Resource.h"
# include "Keyboard.h"
# include "Menu.h"
# include "Dialog.h"
# include "ListView.h"
# include "Settings.h"
# include "Str.h"
# include "Globals.h"

static HACCEL g_hAccelTable_Main;
static HACCEL g_hAccelTable_Static;

void InitKeyboard () {
	// Load static accelerators.
	assert (g_hAccelTable_Static == NULL);
	g_hAccelTable_Static = ::LoadAccelerators (g_hInst, MAKEINTRESOURCE (IDA_STATIC));

	// Load user-defined accelerators.
	assert (g_hAccelTable_Main == NULL);
	const size_t cHotKeysBytes = GetSettingLen ("HotKeys");
	if (cHotKeysBytes != 0) {
		ACCEL * const pAccels = (ACCEL *) alloca (cHotKeysBytes);
		::memset (pAccels, 0, cHotKeysBytes);

		if (GetSetting ("HotKeys", pAccels, cHotKeysBytes) == cHotKeysBytes) {
			const size_t cAccels = cHotKeysBytes/sizeof (ACCEL);
			g_hAccelTable_Main = ::CreateAcceleratorTable (pAccels, cAccels);
			if (g_hAccelTable_Main != NULL)
				return;
		}
	}

	assert (g_hAccelTable_Main == NULL);
	g_hAccelTable_Main = ::LoadAccelerators (g_hInst, MAKEINTRESOURCE (IDA_MAIN));
}

void CloseKeyboard () {
	if (g_hAccelTable_Main)
		::DestroyAcceleratorTable (g_hAccelTable_Main);
	if (g_hAccelTable_Static)
		::DestroyAcceleratorTable (g_hAccelTable_Static);
}

bool TranslateAccelerator (MSG & _msg) {
	if (::TranslateAccelerator (_msg.hwnd, g_hAccelTable_Static, & _msg))
		return true;
	if (::TranslateAccelerator (_msg.hwnd, g_hAccelTable_Main,   & _msg))
		return true;
	return false;
}

//////////////////////////////////////////////////////////

class CEnterHotKey : public CDialog {
  public:
	CEnterHotKey () : CDialog (IDD_EDIT_HOTKEY) {}

	virtual void OnCommand (WORD _wCmd, WORD _wNotifyCode, HWND _hwndCtl) {
		switch (_wCmd) {
			case IDC_HOTKEY:
				if (_wNotifyCode == EN_CHANGE) {
					const DWORD dwHotKey = ::SendDlgItemMessage (m_hWnd, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0);
					EnableDlgItem (IDOK, dwHotKey != 0);
				}
				break;

			case IDOK: {
				const DWORD dwHotKey = ::SendDlgItemMessage (m_hWnd, IDC_HOTKEY, HKM_GETHOTKEY, 0, 0);

				m_accel.fVirt = FVIRTKEY;
				if (((dwHotKey >> 8) & HOTKEYF_ALT) != 0)
					m_accel.fVirt |= FALT;
				if (((dwHotKey >> 8) & HOTKEYF_CONTROL) != 0)
					m_accel.fVirt |= FCONTROL;
				if (((dwHotKey >> 8) & HOTKEYF_SHIFT) != 0)
					m_accel.fVirt |= FSHIFT;

				m_accel.key = dwHotKey & 0xFF;
				m_accel.cmd = 0;

				EndDialog (1);
				break;
			}

			case IDCANCEL:
				EndDialog (0);
				break;
		}
	}

	ACCEL m_accel;
};

//////////////////////////////////////////////////////////

typedef std::vector<ACCEL> accels_t;
typedef std::map<UINT, accels_t> id2accels_t;

static
void _FillId2Accels (HACCEL _hAccelTable, id2accels_t & _id2accels) {
	const size_t cEntries = ::CopyAcceleratorTable (_hAccelTable, NULL, 0);
	ACCEL * const pEntries = (ACCEL *) alloca (cEntries*sizeof (ACCEL));
	::CopyAcceleratorTable (_hAccelTable, pEntries, cEntries);

	for (size_t c = 0; c < cEntries; ++ c) {
		const ACCEL & acc = pEntries [c];
		_id2accels [acc.cmd].push_back (acc);
	}
}

static
void _GetHotKeyName (const ACCEL & _acc, char * _str) {
	// Add Alt/Shift/Ctrl.
	if (_acc.fVirt & FALT)
		::strcat (_str, "Alt+");
	if (_acc.fVirt & FCONTROL)
		::strcat (_str, "Ctrl+");
	if (_acc.fVirt & FSHIFT)
		::strcat (_str, "Shift+");

	// Add key name.
	char strKeyName [32];
	if (_acc.fVirt & FVIRTKEY) {
		switch (_acc.key) {
			case VK_SPACE:  ::strcpy (strKeyName, "Space");    break;
			case VK_ESCAPE: ::strcpy (strKeyName, "Esc");      break;
			case VK_BACK:   ::strcpy (strKeyName, "Back");     break;
			case VK_PRIOR:  ::strcpy (strKeyName, "PageUp");   break;
			case VK_NEXT:   ::strcpy (strKeyName, "PageDown"); break;
			case VK_END:    ::strcpy (strKeyName, "End");      break;
			case VK_HOME:   ::strcpy (strKeyName, "Home");     break;
			case VK_LEFT:   ::strcpy (strKeyName, "Left");     break;
			case VK_UP:     ::strcpy (strKeyName, "Up");       break;
			case VK_RIGHT:  ::strcpy (strKeyName, "Right");    break;
			case VK_DOWN:   ::strcpy (strKeyName, "Down");     break;
			case VK_DELETE: ::strcpy (strKeyName, "Del");      break;
			case VK_INSERT: ::strcpy (strKeyName, "Ins");      break;

			default: {
				const UINT uiScanCode = ::MapVirtualKey (_acc.key, 0);
				::GetKeyNameText (uiScanCode << 16, strKeyName, ARRAY_SIZE (strKeyName));
				break;
			}
		}
	} else {
		strKeyName [0] = reinterpret_cast<const char *> (& _acc.key) [0];
		strKeyName [1] = reinterpret_cast<const char *> (& _acc.key) [1];
		strKeyName [2] = '\0';
	}
	::strcat (_str, strKeyName);

	// Cosmetic fixes to avoid "Ctrl++" or "Ctrl+-".
	const cLen = ::strlen (_str);
	if (cLen >= 2 && _str [cLen - 2] == '+') {
		if (
			_str [cLen - 1] == '+' ||
			_str [cLen - 1] == '-' ||
			_str [cLen - 1] == '='
		)
			_str [cLen - 2] = ' ';
	}
}

static
bool _IsHotKeyEqual (const ACCEL & _accel1, const ACCEL & _accel2) {
	if ((_accel1.fVirt & FVIRTKEY) != (_accel2.fVirt & FVIRTKEY))
		return false;
	if (_accel1.key != _accel2.key)
		return false;
	if ((_accel1.fVirt & (FALT | FCONTROL | FSHIFT)) != (_accel2.fVirt & (FALT | FCONTROL | FSHIFT)))
		return false;
	return true;
}

//////////////////////////////////////////////////////////

class CCustomizeKeyboard;

class CEditHotKeys : public CDialog {
  public:
	CEditHotKeys (CCustomizeKeyboard * _pCustomizeDlg, accels_t & _accels, const string_t & _strCmd, UINT _uiCmd) :
		CDialog (IDD_KEYBOARD_EDIT),
		m_pCustomizeDlg (_pCustomizeDlg),
		m_accels (_accels),
		m_strCmd (_strCmd),
		m_uiCmd (_uiCmd)
	{}

	// Override.
	virtual bool OnInit (HWND _hwndFocus) {
		// Command.
		SetDlgItemText (IDC_COMMAND, m_strCmd.c_str ());

		// List.
		m_hList = GetDlgItem (IDC_LIST);
		ListView_SetExtendedListViewStyleEx (m_hList, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
		AddColumn (m_hList, 0, "");
		_UpdateList ();

		EnableDlgItem (IDC_REMOVE, false);
		EnableDlgItem (IDC_REMOVE_ALL, ! m_accels.empty ());

		return true;
	}

	virtual void OnCommand (WORD _wCmd, WORD _wNotifyCode, HWND _hwndCtl) {
		switch (_wCmd) {
			case IDC_REMOVE_ALL:
				m_accels.clear ();
				EnableDlgItem (IDC_REMOVE_ALL, false);
				_UpdateList ();
				break;

			case IDC_REMOVE: {
				const size_t cSel = ListView_GetSelectionMark (m_hList);
				if (cSel == -1)
					break;
				if (cSel < m_accels.size ()) {
					m_accels.erase (m_accels.begin () + cSel);
					EnableDlgItem (IDC_REMOVE_ALL, ! m_accels.empty ());
					_UpdateList ();
				}
				break;
			}

			case IDC_ADD: {
				CEnterHotKey ehk;
				if (ehk.ShowModal (m_hWnd)) {
					if (_IsHotKeyUnique (ehk.m_accel)) {
						ehk.m_accel.cmd = m_uiCmd;
						m_accels.push_back (ehk.m_accel);

						EnableDlgItem (IDC_REMOVE_ALL, ! m_accels.empty ());
						_UpdateList ();
					}
				}
				break;
			}

			case IDOK:
				EndDialog (1);
				break;
			case IDCANCEL:
				EndDialog (0);
				break;
		}
	}

	virtual bool OnNotify (int _idCtrl, NMHDR * _pInfo) {
		switch (_pInfo->code) {
			case LVN_ITEMCHANGED: {
				const NMLISTVIEW * const pInfo2 = reinterpret_cast<const NMLISTVIEW *> (_pInfo);
				if (pInfo2->uNewState & LVIS_SELECTED)
					EnableDlgItem (IDC_REMOVE, true);
				break;
			}
		}

		return false;
	}

  public:
	CCustomizeKeyboard * const m_pCustomizeDlg;

	HWND m_hList;

	accels_t & m_accels;
	const string_t m_strCmd;
	UINT m_uiCmd;

	void _UpdateList ();
	bool _IsHotKeyUnique (const ACCEL & _accel) const;
};

void CEditHotKeys::_UpdateList () {
	ListView_DeleteAllItems (m_hList);

	for (size_t c = 0; c < m_accels.size (); ++ c) {
		const ACCEL & accel = m_accels [c];

		char strHotKey [128] = "";
		_GetHotKeyName (accel, strHotKey);

		InsertItem (m_hList, c, c, "%s", strHotKey);
	}

	AutoSizeColumns (m_hList);
}

//////////////////////////////////////////////////////////

enum {
	lvCommand = 0,
	lvHotKey  = 1
};

class CCustomizeKeyboard : public CDialog {
  public:
	CCustomizeKeyboard () : CDialog (IDD_KEYBOARD) {}

	// Override.
	virtual bool OnInit (HWND _hwndFocus) {
		// List.
		m_hList = GetDlgItem (IDC_LIST);
		ListView_SetExtendedListViewStyleEx (m_hList, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
		AddColumn (m_hList, lvCommand, "Command");
		AddColumn (m_hList, lvHotKey,  "Hot key(s)");

		_PrepareTables (g_hAccelTable_Main);
		_UpdateList ();

		// Edit.
		EnableDlgItem (IDC_EDIT, false);

		// OK.
		EnableDlgItem (IDOK, false);

		return true;
	}

	virtual void OnCommand (WORD _wCmd, WORD _wNotifyCode, HWND _hwndCtl) {
		switch (_wCmd) {
			case IDC_EDIT: {
				const size_t cSel = ListView_GetSelectionMark (m_hList);
				if (cSel == -1)
					break;

				const UINT uiCmd = GetLParam (m_hList, cSel);
				accels_t & accels = m_id2accels [uiCmd];

				char strCmd [128];
				ListView_GetItemText (m_hList, cSel, 0, strCmd, ARRAY_SIZE (strCmd));

				accels_t accelsNew = accels;
				CEditHotKeys ehk (this, accelsNew, strCmd, uiCmd);
				if (ehk.ShowModal (m_hWnd)) {
					accels.swap (accelsNew);

					// Remove all previous assignments of the hot keys for other commands, if any.
					const size_t cAccels = accels.size ();
					for (size_t c = 0; c < cAccels; ++ c)
						_RemoveHotKey (accels [c], uiCmd);

					_UpdateList (uiCmd);
					EnableDlgItem (IDOK, true);
				}
				break;
			}

			case IDC_RESTORE_DEFAULTS: {
				const HACCEL hAccelTable_Main = ::LoadAccelerators (g_hInst, MAKEINTRESOURCE (IDA_MAIN));
				_PrepareTables (hAccelTable_Main);
				::DestroyAcceleratorTable (hAccelTable_Main);

				_UpdateList ();

				EnableDlgItem (IDC_EDIT, false);
				EnableDlgItem (IDOK,     true);

				break;
			}

			case IDOK: {
				// Estimate size of new table of accelerators.
				size_t cAccels = 0;
				id2accels_t::const_iterator i;
				for (i = m_id2accels.begin (); i != m_id2accels.end (); ++ i) {
					const accels_t & accels = i->second;
					cAccels += accels.size ();
				}

				// Create and save the new table of accelerators.
				ACCEL * const pAccels = (ACCEL *) alloca (sizeof (ACCEL)*cAccels);

				size_t cAccel = 0;
				for (i = m_id2accels.begin (); i != m_id2accels.end (); ++ i) {
					const accels_t & accels = i->second;
					for (size_t c = 0; c < accels.size (); ++ c)
						pAccels [cAccel ++] = accels [c];
				}

				if (g_hAccelTable_Main)
					::DestroyAcceleratorTable (g_hAccelTable_Main);
				g_hAccelTable_Main = ::CreateAcceleratorTable (pAccels, cAccels);

				SaveSetting ("HotKeys", pAccels, cAccels*sizeof (ACCEL));

				EndDialog (1);
				break;
			}

			case IDCANCEL:
				EndDialog (0);
				break;
		}
	}

	virtual bool OnNotify (int _idCtrl, NMHDR * _pInfo) {
		switch (_pInfo->code) {
			case NM_CLICK:
			case LVN_KEYDOWN:
				EnableDlgItem (IDC_EDIT, ListView_GetSelectionMark (m_hList) != -1);
				break;

			case NM_RETURN:
			case NM_DBLCLK:
				::SendMessage (m_hWnd, WM_COMMAND, IDC_EDIT, 0);
				break;
		}
		return false;
	}

	bool IsHotKeyUnique (const ACCEL & _accel, UINT _uiCmdExcept) const;

  private:
	HWND m_hList;

	id2accels_t m_id2accels;

	typedef std::set<UINT> cmds_t;
	cmds_t m_cmdsNotInMenu;

	void _PrepareTables (HACCEL _hAccelTable);
	void _GetCmdName (UINT _uiCmd, char * _strName, size_t _cNameMax) const;
	void _UpdateList (UINT _uiCmdSel = -1);
	void _EnumMenuItems (HMENU _hMenu, const char * _strMenuName, UINT _uiCmdSel, size_t & _cItem);
	void _GetHotKeysNames (UINT _uiCmd, char * _str) const;
	void _RemoveHotKey (const ACCEL & _accel, UINT _uiCmdExcept);
};

void CCustomizeKeyboard::_GetHotKeysNames (UINT _uiCmd, char * _str) const {
	const id2accels_t::const_iterator i = m_id2accels.find (_uiCmd);
	if (i == m_id2accels.end () || i->second.empty ()) {
		::strcpy (_str, "-");
		return;
	}

	const accels_t & accels = i->second;

	_str [0] = '\0';
	for (size_t a = 0; a < accels.size (); ++ a) {
		const ACCEL & acc = accels [a];

		if (_str [0] != '\0')
			::strcat (_str, ", ");
		_GetHotKeyName (acc, _str);
	}
}

bool CEditHotKeys::_IsHotKeyUnique (const ACCEL & _accel) const {
	// Check for 'static' accelerators.
	const size_t cStaticEntries = ::CopyAcceleratorTable (g_hAccelTable_Static, NULL, 0);
	ACCEL * const pStaticEntries = (ACCEL *) alloca (cStaticEntries*sizeof (ACCEL));
	::CopyAcceleratorTable (g_hAccelTable_Static, pStaticEntries, cStaticEntries);
	size_t c;
	for (c = 0; c < cStaticEntries; ++ c) {
		const ACCEL & accel = pStaticEntries [c];
		if (_IsHotKeyEqual (accel, _accel)) {
			InfoMsgBox ("Sorry, this hot key cannot be re-assigned.");
			return false;
		}
	}

	// Check hot keys for the same command to avoid repeating.
	const size_t cAccels = m_accels.size ();
	for (c = 0; c < cAccels; ++ c) {
		const ACCEL & accel = m_accels [c];
		if (_IsHotKeyEqual (accel, _accel))
			return false;
	}

	// Check hot keys for other commands.
	return m_pCustomizeDlg->IsHotKeyUnique (_accel, m_uiCmd);
}

bool CCustomizeKeyboard::IsHotKeyUnique (const ACCEL & _accel, UINT _uiCmdExcept) const {
	for (id2accels_t::const_iterator i = m_id2accels.begin (); i != m_id2accels.end (); ++ i) {
		if (i->first == _uiCmdExcept)
			continue;

		const accels_t & accels = i->second;
		const size_t cAccels = accels.size ();
		for (size_t c = 0; c < cAccels; ++ c) {
			const ACCEL & accel = accels [c];
			if (_IsHotKeyEqual (_accel, accel)) {
				char strCmd [128];
				_GetCmdName (accel.cmd, strCmd, ARRAY_SIZE (strCmd));
				return YesNoMsgBox ("This hot key is already assigned to the command '%s'. Are you sure you want to re-assign it?", strCmd);
			}
		}
	}

	return true;
}

void CCustomizeKeyboard::_RemoveHotKey (const ACCEL & _accel, UINT _uiCmdExcept) {
	for (id2accels_t::iterator i = m_id2accels.begin (); i != m_id2accels.end (); ++ i) {
		if (i->first == _uiCmdExcept)
			continue;

		accels_t & accels = i->second;
		size_t cAccels = accels.size ();
		for (size_t c = 0; c < cAccels; ++ c) {
			const ACCEL & accel = accels [c];
			if (_IsHotKeyEqual (_accel, accel)) {
				accels.erase (accels.begin () + c);
				c = 0;
				assert (cAccels >= 1);
				-- cAccels;
			}
		}
	}
}

void CCustomizeKeyboard::_PrepareTables (HACCEL _hAccelTable) {
	m_id2accels.clear ();
	_FillId2Accels (_hAccelTable, m_id2accels);

	for (id2accels_t::const_iterator i = m_id2accels.begin (); i != m_id2accels.end (); ++ i)
		m_cmdsNotInMenu.insert (i->first);
}

void CCustomizeKeyboard::_GetCmdName (UINT _uiCmd, char * _strName, size_t _cNameMax) const {
	const size_t cItems = ListView_GetItemCount (m_hList);
	for (size_t c = 0; c < cItems; ++ c) {
		if ((UINT) GetLParam (m_hList, c) == _uiCmd) {
			ListView_GetItemText (m_hList, c, 0, _strName, _cNameMax);
			return;
		}
	}

	_strName [0] = '\0';
}

void CCustomizeKeyboard::_UpdateList (UINT _uiCmdSel) {
	ListView_DeleteAllItems (m_hList);

	// Add commands from main menu items.
	size_t cItem = 0;
	_EnumMenuItems (g_MainMenu, NULL, _uiCmdSel, cItem);

	// Add other commands.
	for (cmds_t::const_iterator i = m_cmdsNotInMenu.begin (); i != m_cmdsNotInMenu.end (); ++ i) {
		const UINT uiCmd = * i;
		const char * strCmd = NULL;
		switch (uiCmd) {
			case ID_STICK_TOGGLE:
				strCmd = "Toggle Stick to Neighbours";
				break;

			default:
				assert (false);
				strCmd = "<%d>"; break;
		}
		InsertItem (m_hList, cItem, uiCmd, strCmd, uiCmd);

		char strAcc [128];
		_GetHotKeysNames (uiCmd, strAcc);
		SetSubItemText (m_hList, cItem, lvHotKey, "%s", strAcc);

		if (_uiCmdSel != -1 && uiCmd == _uiCmdSel)
			SetSelection (m_hList, cItem);

		++ cItem;
	}

	AutoSizeColumns (m_hList);
	EnsureSelectionVisible (m_hList);
}

void CCustomizeKeyboard::_EnumMenuItems (HMENU _hMenu, const char * _strMenuName, UINT _uiCmdSel, size_t & _cItem) {
	const size_t cItems = ::GetMenuItemCount (_hMenu);
	if (cItems == -1)
		return;

	for (size_t c = 0; c < cItems; ++ c) {
		if (IsSeparator (_hMenu, c))
			continue;

		// Prepare name of menu item.
		char strName [128];
		::GetMenuString (_hMenu, c, strName, ARRAY_SIZE (strName), MF_BYPOSITION);
		// Remove \t and the built-in accelerator name.
		char * const pTab = lstrchr (strName, '\t');
		if (pTab)
			* pTab = '\0';
		// Remove all ampersands.
		while (true) {
			char * const pAmpersand = lstrchr (strName, '&');
			if (pAmpersand == NULL)
				break;

			::memmove (pAmpersand, pAmpersand + 1, ::strlen (pAmpersand + 1) + 1);
		}

		char strMenuName [128];
		if (_strMenuName != NULL) {
			::strcpy (strMenuName, _strMenuName);
			::strcat (strMenuName, " | ");
			::strcat (strMenuName, strName);
		} else
			::strcpy (strMenuName, strName);

		const HMENU hSubMenu = ::GetSubMenu (_hMenu, c);
		if (hSubMenu == g_hmenuWTR)
			continue;
		if (hSubMenu != NULL) {
			_EnumMenuItems (hSubMenu, strMenuName, _uiCmdSel, _cItem);
			continue;
		}

		const UINT uiCmd = ::GetMenuItemID (_hMenu, c);
		if (ID_FILE_RECENT_0 <= uiCmd && uiCmd < ID_FILE_RECENT_0 + 100)
			continue;
		if (ID_VIEW_SCALE_0 <= uiCmd && uiCmd < ID_VIEW_SCALE_0 + 100)
			continue;

		// Command.
		InsertItem (m_hList, _cItem, uiCmd, "%s", strMenuName);

		// Hot keys.
		char strAcc [128];
		_GetHotKeysNames (uiCmd, strAcc);
		SetSubItemText (m_hList, _cItem, lvHotKey, "%s", strAcc);

		if (_uiCmdSel != -1 && uiCmd == _uiCmdSel)
			SetSelection (m_hList, _cItem);

		++ _cItem;

		m_cmdsNotInMenu.erase (uiCmd);
	}
}

void CustomizeKeyboard (HWND _hWnd) {
	CCustomizeKeyboard ck;
	ck.ShowModal (_hWnd);

	UpdateMenuAccelerators (g_MainMenu);
}

/////////////////////////////////////////////////////////////

static
void _UpdateMenuAccelerators (HMENU _hMenu, id2accels_t & _id2accels) {
	const size_t cItems = ::GetMenuItemCount (_hMenu);
	if (cItems == -1)
		return;

	for (size_t c = 0; c < cItems; ++ c) {
		if (IsSeparator (_hMenu, c))
			continue;

		const HMENU hSubMenu = ::GetSubMenu (_hMenu, c);
		if (hSubMenu != NULL) {
			_UpdateMenuAccelerators (hSubMenu, _id2accels);
			continue;
		}

		// Get name of menu item.
		char strName [128] = "";
		::GetMenuString (_hMenu, c, strName, ARRAY_SIZE (strName), MF_BYPOSITION);
		if (strName [0] == '\0')
			continue;
		// Remove \t and current accelerator name.
		char * const pTab = lstrchr (strName, '\t');
		if (pTab)
			* pTab = '\0';

		const UINT uiCmd = ::GetMenuItemID (_hMenu, c);
		const id2accels_t::const_iterator i = _id2accels.find (uiCmd);
		if (i != _id2accels.end () && ! i->second.empty ()) {
			const accels_t & accels = i->second;

			size_t cBestAccel = 0;
			for (size_t c = 0; c < accels.size (); ++ c) {
				const ACCEL & accel = accels [c];
				if ((accel.fVirt & FVIRTKEY) == 0 || (0x30 <= accel.key && accel.key <= 0x5a)) {
					cBestAccel = c;
					break;
				}
			}
			assert (cBestAccel < accels.size ());

			// Append new accelerator name.
			char strAcc [128] = "";
			_GetHotKeyName (accels [cBestAccel], strAcc);

			if (strAcc [0] != '\0') {
				::strcat (strName, "\t");
				::strcat (strName, strAcc);
			}
		}

		MENUITEMINFO mii;
		::memset (& mii, 0, sizeof (mii));
		mii.cbSize     = sizeof (mii);
		mii.fMask      = MIIM_STRING;
		mii.dwTypeData = strName;
		mii.cch        = ::strlen (strName);
		::SetMenuItemInfo (_hMenu, c, TRUE, & mii);
	}
}

void UpdateMenuAccelerators (HMENU _hMenu) {
	assert (_hMenu);

	id2accels_t id2accels;
	_FillId2Accels (g_hAccelTable_Main, id2accels);

	_UpdateMenuAccelerators (_hMenu, id2accels);
}
